﻿using Inovout.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Linq;

namespace Inovout.Data.Redis
{
    public class RedisReplyParser : IHttpResponseParser
    {
        public RedisCommandScript[] CommandScripts { get; private set; }
        public RedisReplyParser(RedisCommandScript[] commandScripts)
        {
            CommandScripts = commandScripts;
            parseFuncs = new Dictionary<byte, Func<Stream, object>>();
            parseFuncs.Add(singeLineSymbolBuffer, new Func<Stream, object>(ParseSingleLineReply));
            parseFuncs.Add(errorMessageSymbolBuffer, new Func<Stream, object>(ParseErrorMessageReply));
            parseFuncs.Add(integerNumberSymbolBuffer, new Func<Stream, object>(ParseIntegerNumberReply));
            parseFuncs.Add(bulkSymbolBuffer, new Func<Stream, object>(ParseBulkReply));
            parseFuncs.Add(multiBulkSymbolBuffer, new Func<Stream, object>(ParseMultiBulkReply));
        }

        private static readonly byte singeLineSymbolBuffer;
        private static readonly byte errorMessageSymbolBuffer;
        private static readonly byte integerNumberSymbolBuffer;
        private static readonly byte bulkSymbolBuffer;
        private static readonly byte multiBulkSymbolBuffer;

        private readonly Dictionary<byte, Func<Stream, object>> parseFuncs;
        static RedisReplyParser()
        {
            singeLineSymbolBuffer = Encoding.UTF8.GetBytes("+")[0];
            errorMessageSymbolBuffer = Encoding.UTF8.GetBytes("-")[0];
            integerNumberSymbolBuffer = Encoding.UTF8.GetBytes(":")[0];
            bulkSymbolBuffer = Encoding.UTF8.GetBytes("$")[0];
            multiBulkSymbolBuffer = Encoding.UTF8.GetBytes("*")[0];

        }

        private object ParseMultiBulkReply(Stream stream)
        {
            throw new NotImplementedException();
        }
        public object ParseBulkReply(Stream stream)
        {
            CrlfFlag flag = CrlfFlag.None;
            byte[] buffer = new byte[1];
            List<byte> statusBuffer = new List<byte>();
            while (stream.CanRead)
            {
                stream.Read(buffer, 0, 1);
                if (!RedisBlobCommandParameter.CrlfButffer.Contains(buffer[0]))
                {
                    statusBuffer.Add(buffer[0]);
                }
                else if (RedisBlobCommandParameter.CrlfButffer[0].Equals(buffer[0]))
                {
                    flag = CrlfFlag.Cr;
                }
                else if (RedisBlobCommandParameter.CrlfButffer[1].Equals(buffer[0]))
                {
                    flag |= CrlfFlag.Lf;
                }
                if (flag == (CrlfFlag.Cr | CrlfFlag.Lf))
                {
                    break;
                }
            }
            int replyLength = int.Parse(Encoding.UTF8.GetString(statusBuffer.ToArray()));
            statusBuffer.Clear();
            flag = CrlfFlag.None;
            while (stream.CanRead)
            {
                stream.Read(buffer, 0, 1);
                if (!RedisBlobCommandParameter.CrlfButffer.Contains(buffer[0]))
                {
                    statusBuffer.Add(buffer[0]);
                }
                else if (RedisBlobCommandParameter.CrlfButffer[0].Equals(buffer[0]))
                {
                    flag = CrlfFlag.Cr;
                }
                else if (RedisBlobCommandParameter.CrlfButffer[1].Equals(buffer[0]))
                {
                    flag |= CrlfFlag.Lf;
                }
                if (flag == (CrlfFlag.Cr | CrlfFlag.Lf))
                {
                    break;
                }
            }
            var commandScript = CommandScripts.First(cs => !cs.CommandResult.IsCompleted);
            commandScript.CommandResult.SetContent(Encoding.UTF8.GetString(statusBuffer.ToArray()));
            return commandScript.CommandResult.Content;
  
        }
        public object ParseIntegerNumberReply(Stream stream)
        {
            CrlfFlag flag = CrlfFlag.None;
            byte[] buffer = new byte[1];
            List<byte> statusBuffer = new List<byte>();
            while (stream.CanRead)
            {
                stream.Read(buffer, 0, 1);
                if (!RedisBlobCommandParameter.CrlfButffer.Contains(buffer[0]))
                {
                    statusBuffer.Add(buffer[0]);
                }
                else if (RedisBlobCommandParameter.CrlfButffer[0].Equals(buffer[0]))
                {
                    flag = CrlfFlag.Cr;
                }
                else if (RedisBlobCommandParameter.CrlfButffer[1].Equals(buffer[0]))
                {
                    flag |= CrlfFlag.Lf;
                }
                if (flag == (CrlfFlag.Cr | CrlfFlag.Lf))
                {
                    break;
                }

            }
            var commandScript = CommandScripts.First(cs => !cs.CommandResult.IsCompleted);
            commandScript.CommandResult.SetContent(int.Parse(Encoding.UTF8.GetString(statusBuffer.ToArray()))); 
            return commandScript.CommandResult.Content;
        }
        private enum CrlfFlag
        {
            None=0,
            Cr=1,
            Lf=2,
        }
        public object ParseSingleLineReply(Stream stream)
        {
            //Status reply
            CrlfFlag flag = CrlfFlag.None;
            byte[] buffer = new byte[1];
            List<byte> statusBuffer = new List<byte>();
            while (stream.CanRead)
            {
                stream.Read(buffer, 0, 1);
                if (!RedisBlobCommandParameter.CrlfButffer.Contains(buffer[0]))
                {
                    statusBuffer.Add(buffer[0]);
                }
                else if (RedisBlobCommandParameter.CrlfButffer[0].Equals(buffer[0]))
                {
                    flag = CrlfFlag.Cr;
                }
                else if (RedisBlobCommandParameter.CrlfButffer[1].Equals(buffer[0]))
                {
                    flag |= CrlfFlag.Lf;
                }
                if (flag == (CrlfFlag.Cr | CrlfFlag.Lf))
                {
                    break;
                }

            }
            var commandScript = CommandScripts.First(cs => !cs.CommandResult.IsCompleted);
            commandScript.CommandResult.SetContent(Encoding.UTF8.GetString(statusBuffer.ToArray()));
            return commandScript.CommandResult.Content;
        }
        public object ParseErrorMessageReply(Stream stream)
        {
            throw new NotImplementedException();
        }
        public object Parse(Stream stream)
        {

            //用单行回复，回复的第一个字节将是“+”
            //错误消息，回复的第一个字节将是“-”
            //整型数字，回复的第一个字节将是“:”
            //批量回复，回复的第一个字节将是“$”
            //多个批量回复，回复的第一个字节将是“*”
            byte[] buffer = new byte[1];
            while (stream.Read(buffer, 0, 1)>0)
            {                ;
                if (parseFuncs.Keys.Contains(buffer[0]))
                {
                    parseFuncs[buffer[0]](stream);
                }
            }
            return null;
        }

        private WaitHandle asyncWaithHandler;
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                return asyncWaithHandler;
            }
            set
            {
                asyncWaithHandler = value;
                foreach (var commandScript in CommandScripts)
                {
                    commandScript.SetWaitHandler(asyncWaithHandler);
                }
            }
        }
    }
}
